package sqlparser;

import modeller.databasedesignmodel.Attribute;
import modeller.databasedesignmodel.relation.Relation;
import net.sf.jsqlparser.expression.*;
import net.sf.jsqlparser.expression.operators.arithmetic.*;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.conditional.OrExpression;
import net.sf.jsqlparser.expression.operators.relational.*;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.SubSelect;

import java.util.*;

/**
 * Created by:  Jason Ye
 * Date:        03/06/2012
 * Time:        14:20
 */
public class QuePEDExpressionVisitor implements ExpressionVisitor, ItemsListVisitor {

    private HashSet<Attribute> attributesForQuery;
    private Relation relation;
    private boolean isRange = false;

    public QuePEDExpressionVisitor(Relation relation) {
        this.attributesForQuery = new HashSet<Attribute>();
        this.relation = relation;
    }

    public boolean isRange() {
        return isRange;
    }

    public void resetExpressionVisitor() {
        attributesForQuery = new HashSet<Attribute>();
    }


    @Override
    public void visit(Function function) {

        // execute for all expressions in function
        ExpressionList expressionList = function.getParameters();

        expressionList.accept(this);
    }

    @Override
    public void visit(ExpressionList expressionList) {
        List<Expression> expressions = expressionList.getExpressions();
        Iterator<Expression> it = expressions.listIterator();
        while (it.hasNext()) {
            it.next().accept(this);
        }
    }

    @Override
    public void visit(Parenthesis parenthesis) {
        parenthesis.getExpression().accept(this);
    }


    @Override
    public void visit(InverseExpression inverseExpression) {
        inverseExpression.getExpression().accept(this);
    }

    @Override
    public void visit(Addition addition) {
        addition.getLeftExpression().accept(this);
        addition.getRightExpression().accept(this);

    }

    @Override
    public void visit(Division division) {
        division.getLeftExpression().accept(this);
        division.getRightExpression().accept(this);
    }

    @Override
    public void visit(Multiplication multiplication) {
        multiplication.getLeftExpression().accept(this);
        multiplication.getRightExpression().accept(this);
    }

    @Override
    public void visit(Subtraction subtraction) {
        subtraction.getLeftExpression().accept(this);
        subtraction.getRightExpression().accept(this);
    }

    @Override
    public void visit(AndExpression andExpression) {
        andExpression.getLeftExpression().accept(this);
        andExpression.getRightExpression().accept(this);
    }

    @Override
    public void visit(OrExpression orExpression) {
        orExpression.getLeftExpression().accept(this);
        orExpression.getRightExpression().accept(this);
    }

    @Override
    public void visit(Between between) {
        isRange = true;
        between.getLeftExpression().accept(this);
    }

    @Override
    public void visit(EqualsTo equalsTo) {
        equalsTo.getLeftExpression().accept(this);
        equalsTo.getRightExpression().accept(this);
    }

    @Override
    public void visit(GreaterThan greaterThan) {
        greaterThan.getLeftExpression().accept(this);
        greaterThan.getRightExpression().accept(this);
        isRange = true;
    }

    @Override
    public void visit(GreaterThanEquals greaterThanEquals) {
        greaterThanEquals.getLeftExpression().accept(this);
        greaterThanEquals.getRightExpression().accept(this);
        isRange = true;
    }

    @Override
    public void visit(InExpression inExpression) {

        inExpression.getLeftExpression().accept(this);
        inExpression.getItemsList().accept(this);

    }

    @Override
    public void visit(IsNullExpression isNullExpression) {

        isNullExpression.getLeftExpression().accept(this);

    }

    @Override
    public void visit(LikeExpression likeExpression) {
        likeExpression.getLeftExpression().accept(this);
        likeExpression.getRightExpression().accept(this);
    }

    @Override
    public void visit(MinorThan minorThan) {
        minorThan.getLeftExpression().accept(this);
        minorThan.getRightExpression().accept(this);
    }

    @Override
    public void visit(MinorThanEquals minorThanEquals) {
        minorThanEquals.getLeftExpression().accept(this);
        minorThanEquals.getRightExpression().accept(this);
    }

    @Override
    public void visit(NotEqualsTo notEqualsTo) {
        notEqualsTo.getLeftExpression().accept(this);
        notEqualsTo.getRightExpression().accept(this);
    }

    @Override
    public void visit(Column column) {

        attributesForQuery.add(getAttributeFromName(column.getColumnName()));

    }

    private Attribute getAttributeFromName(String columnName) {

        return relation.getAttribute(columnName);

    }

    /*
    redundant expressions - cannot evaluate to a table name or table attribute name
    */

    @Override
    public void visit(NullValue nullValue) {

        // do nothing - this cannot evaluate to a table or column name
    }

    @Override
    public void visit(JdbcParameter jdbcParameter) {
        // do nothing - this cannot evaluate to a table or column name
    }

    @Override
    public void visit(DoubleValue doubleValue) {
        // do nothing - this cannot evaluate to a table or column name
    }

    @Override
    public void visit(LongValue longValue) {
        // do nothing - this cannot evaluate to a table or column name
    }

    @Override
    public void visit(DateValue dateValue) {
        // do nothing - this cannot evaluate to a table or column name
    }

    @Override
    public void visit(TimeValue timeValue) {
        // do nothing - this cannot evaluate to a table or column name
    }

    @Override
    public void visit(TimestampValue timestampValue) {
        // do nothing - this cannot evaluate to a table or column name
    }

    @Override
    public void visit(StringValue stringValue) {
        // do nothing - this is not a attribute name
    }


    @Override
    public void visit(SubSelect subSelect) {
        // do nothing - subselects not allowed
    }


    @Override
    public void visit(CaseExpression caseExpression) {

        // do nothing, case expressions not supported
    }

    @Override
    public void visit(WhenClause whenClause) {

        // do nothing, case / when / then expressions not supported

    }

    @Override
    public void visit(ExistsExpression existsExpression) {

        existsExpression.getRightExpression().accept(this);
    }

    @Override
    public void visit(AllComparisonExpression allComparisonExpression) {

//do nothing - this shouldn't add to the currentSQLQuery attributes as it's a aggregate function
    }

    @Override
    public void visit(AnyComparisonExpression anyComparisonExpression) {
//do nothing - this shouldn't add to the currentSQLQuery attributes as it's a aggregate function
    }


    @Override
    public void visit(Concat concat) {
        //do nothing - this doesn't add to the currentSQLQuery attributes in WHERE condition
    }

    @Override
    public void visit(Matches matches) {
        matches.getLeftExpression().accept(this);
        matches.getRightExpression().accept(this);
    }

    @Override
    public void visit(BitwiseAnd bitwiseAnd) {
        bitwiseAnd.getLeftExpression().accept(this);
        bitwiseAnd.getRightExpression().accept(this);
    }

    @Override
    public void visit(BitwiseOr bitwiseOr) {
        bitwiseOr.getLeftExpression().accept(this);
        bitwiseOr.getRightExpression().accept(this);
    }

    @Override
    public void visit(BitwiseXor bitwiseXor) {
        bitwiseXor.getLeftExpression().accept(this);
        bitwiseXor.getRightExpression().accept(this);
    }


    public HashSet<Attribute> getAttributesForQuery() {
        return attributesForQuery;
    }
}
